package gbench.appdemo.crawler.junit.app;

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Stream;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryVisitor;

import gbench.common.tree.LittleTree.IRecord;

import gbench.appdemo.crawler.CrawlerApplication;
import gbench.appdemo.crawler.analyzer.lexer.LexemeAttribute;
import gbench.appdemo.crawler.srch.SrchUtils;

import static gbench.appdemo.crawler.srch.SrchUtils.*;
import static gbench.common.tree.LittleTree.IRecord.*;
import static gbench.common.tree.LittleTree.*;

/**
 * 
 * @author gbench
 *
 */
public class JunitCrawlerAppQuery {
    
    /**
     * 检索查询
     */
    @Test
    public void foo() {
        final var engine = app.new FileSrchEngine();
        engine.initialize();
        var rec =REC("symbol*","*所*","file","月亮和六便士.txt"); // 检索条件
        rec = REC(
            "+symbol*","所以"
            ,"group1", A(// 文件查询组
                REC("file*","大*"),
                REC("-file","老舍文集.txt"))
            ,"+snapfile*","*朱自清*"
        );// 分组
        
        final var query = rec.collect(bool_query_clc);
        System.out.println(query);
        final var qvisitor = new QueryVisitor() {
            public void consumeTerms(Query query, Term... terms) { // terms 的遍历访问
                Stream.of(terms).forEach(term->{
                    System.out.println(MFT("\t[query:{0}],\tclass:{1},\tterm:{2}",query,query.getClass().getSimpleName(),term));
                });
            }
        };
        query.clauses().forEach(e->{
            System.out.println(MFT("{0}\t{1}",e.getQuery(),e.getOccur().name()));
            e.getQuery().visit(qvisitor);
        });
        
        engine.lookup(query, 10000).stream()
        .map(e->e.str("file")).distinct()
        .forEach(System.out::println);
    }
    
    /**
     * 检索查询
     */
    @Test
    public void query_file() {
        final var engine = app.new FileSrchEngine();
        engine.initialize();
        engine.setIndexHome("D:\\sliced\\tmp\\crawlerindexes");// 设置索引文件位置
        System.out.println(engine.getIndexHome());
        var rec = REC("file*","牛顿*");// 文件名的部分匹配
        //rec = REC("text","C:\\Users\\gbench\\Desktop\\中国笔记\\逻辑散文\\images\\历史人物\\牛顿.jpg");// 完整行的匹配
        //rec = REC("text*","*逻辑散文*");// 完整行的匹配
        engine.lookup(rec.collect(bool_query_clc),100).stream()
        //.map(e->e.str("file")).distinct()
        .forEach(System.out::println);
    }
    
    /**
     * 使用analyzer 对text进行分词
     * 
     * @param analyzer 分词器
     * @param fieldName 分词字段
     * @param text 分词文本
     * 
     * @return 分词结果序列:[{value:单词,start:单词的开始字符的段落偏移(包含),end:单词的结尾字符的段落偏移(包含)}] 
     */
    public static List<IRecord> splits(Analyzer analyzer,String text) {
        return splits(analyzer,UUID.randomUUID().toString(),text);
    }
    
    /**
     * 使用analyzer 对text进行分词
     * 
     * @param analyzer 分词器
     * @param fieldName 分词字段：任意名称，用于对分词活动进行标记。
     * @param text 分词文本
     * 
     * @return 分词结果序列:[{value,start,end}] 
     */
    private static List<IRecord> splits(final Analyzer analyzer, final String fieldName, final String text) {
        final var tokens = new LinkedList<IRecord>();// 分词结果序列
        try {
            
            final var tokenStream = analyzer.tokenStream(fieldName, text); // 获得tokenStream对象
            
            final var charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class); // 添加一个引用，可以获得每个关键词
            final var offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class); // 添加一个偏移量的引用，记录了关键词的开始位置以及结束位置
            final var lexemeAttribute = tokenStream.addAttribute(LexemeAttribute.class); // // 词素属性：包含分词的分类category与解释meaning属性
            
            // 将指针调整到列表的头部
            tokenStream.reset();
            
            // 遍历关键词列表，通过incrementToken方法判断列表是否结束
            while (tokenStream.incrementToken()) {
                final var rec = REC(
                    "start",    offsetAttribute.startOffset(),  // 开始位置
                    "end",      offsetAttribute.endOffset(),  // 结尾位置
                    "value",    charTermAttribute.toString(), // 词法符号
                    "lexeme",   lexemeAttribute.getLexeme() // 分词对象: 词位，词素(最小的意义单位)
                );// 分词记录
                rec.add("type",rec.str("value").matches("[~!@#$%^&*\\(\\)\\s\\.。:;]+")?"symbol":"word");// 加入符号类型的
                tokens.add(rec);
            }// while
            
            tokenStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        analyzer.close();
        return tokens;
    }
    
    /**
     * 分词活动
     */
    @Test
    public void bar() {
        final var engine = app.new FileSrchEngine();
        final var text = "泉州地处闽南，民丰物饶，盛紘在这里任同知数年，协理分掌地方盐、粮、河工、水利以及清理军籍、抚绥民夷等事务，多有政绩，这几年知府换了三任，他却在原任上升了品级，盛紘颇会做人，与当地士绅官吏多有交好，闻得盛大人要升迁，这几日便人人争着给他设宴践行，盛紘不便推脱，连日应酬，把家中收拾行装举家迁移之事托付于太太王氏。";
        final var analyzer = engine.getYuhuanAnalyzer("C:\\Users\\gbench\\Desktop\\中国笔记\\逻辑词汇");// 获取分词器
        final var ai = new AtomicInteger(1);
        splits(analyzer, text).forEach(token->{ // 分词
            System.out.println(ai.getAndIncrement()+")\t"+token);
        });
    }
    
    /**
     * 检索查询:遍历索引文件
     */
    @Test
    public void quz() {
        final var engine = app.new FileSrchEngine();
        engine.initialize();
        Function<String,IRecord> s2r = line->{ // 把一个 Record的字符串转换成IRecord
            final var r = REC();
            Arrays.asList(line.split("\\t+")).forEach(item->{
                final var ss = item.split(":");
                r.add(ss[0],ss[1]);
            });
            return r;
        };
        
        try {
            final var ai = new AtomicInteger(1);
            engine.lookup("", 1000000).stream().sorted((a,b)->a.str("symbol").compareTo(b.str("symbol")))
            //.filter(e->e.str("symbol").equals("盛"))
            .sorted((a,b)->{
                final var ap = a.get("position",s2r);
                final var bp = b.get("position",s2r);
                final var t = ap.i4("rownum")-bp.i4("rownum");
                return t==0 ? (ap.i4("start")-bp.i4("start")) :t;
            }).forEach(e->{
                System.out.println(ai.getAndIncrement()+")\t"+e);
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        engine.uninitialize();
    }
    
    /**
     * 创建索引
     */
    @Test
    public void index() {
        final var engine = app.new FileSrchEngine();
        System.out.println("indexHome:"+engine.getIndexHome());
        engine.initialize();
        app.indexFiles("C:\\Users\\gbench\\Desktop\\中国笔记\\逻辑散文\\novel\\archive", status->{
            System.out.println(status);
        });
    }// index
    
    /**
     * 文档遍历
     */
    @Test
    public void quz1() {
        
        final var engine = app.new FileSrchEngine();
        engine.initialize();
        final var ai = new AtomicInteger(1);
        engine.docStream().map(SrchUtils::doc2rec).forEach(e->{
            System.out.println(ai.getAndIncrement()+")\t"+e);
        });
        engine.uninitialize();
    }
    
    public String indexHome = "indexes";// 索引文件的路径
    public String corpusHome = "C:\\Users\\gbench\\Desktop\\中国笔记\\逻辑词汇";// 语料库的词汇目录
    public String snapHome = "D:\\sliced\\tmp\\crawlersnap";// 快照文件根目录
    final CrawlerApplication app = new CrawlerApplication(indexHome, corpusHome,snapHome);

}
